#include "kernel_operator.h"
#include "op_common.h"
namespace AscendC {

template <typename _DT_X, typename _DT_Y> class Kernel {
public:
  TPipe pipe;
  int64_t size = 0;
  int64_t tileLength = 0;
  int64_t finalLength = 0;
  DefInTensor(X);
  DefOutTensor(Y);

  // DefBufVECCALC(CASTED_X);
  // DefBufVECCALC(CASTED_Y);

  // DefBufVECCALC(TEMP);
  // DefBufVECCALC(TEMP2);

public:
  __aicore__ inline Kernel() {}

  template <typename T>
  __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, T tiling_data) {
    this->size = tiling_data.size;
    this->tileLength = tiling_data.tileLength;
    this->finalLength = tiling_data.finalLength;
    // 切分global
    GSetBuffer(X, x, 0, tiling_data.size);
    GSetBuffer(Y, y, 0, tiling_data.size);
    // 初始化队列
    InitQueueSimple(X, tileLength);
    InitQueueSimple(Y, tileLength);
    // 初始化BUF
    // InitTBufBuffer(TEMP, tileLength * sizeof(float));
    // InitTBufBuffer(TEMP2, tileLength * sizeof(float));
    // if constexpr (std::is_same_v<_DT_X, half>) {
    //   InitTBufBuffer(CASTED_X, tileLength * sizeof(float));
    //   InitTBufBuffer(CASTED_Y, tileLength * sizeof(float));
    // }
  }

  template <typename Scalar, int32_t c>
  __aicore__ inline Scalar _poevl(const Scalar x, const Scalar *a) {
    Scalar ret = a[0];
    for (auto i = 1; i <= c; ++i) {
      ret = ret * x;
      ret = ret + a[i];
    }
    return ret;
  }

  template <typename Scalar, int32_t c>
  __aicore__ inline Scalar _p1evl(const Scalar x, const Scalar *a) {
    Scalar ret = a[0];
    ret = ret + x;
    for (auto i = 1; i < c; ++i) {
      ret = ret * x;
      ret = ret + a[i];
    }
    return ret;
  }

  template <typename Scalar>
  __aicore__ inline Scalar generic_dawsn_interval_1(const Scalar &x) {
    // Rational approximation on [0, 3.25)
    const Scalar AN[] = {
        Scalar(1.13681498971755972054E-11), Scalar(8.49262267667473811108E-10),
        Scalar(1.94434204175553054283E-8),  Scalar(9.53151741254484363489E-7),
        Scalar(3.07828309874913200438E-6),  Scalar(3.52513368520288738649E-4),
        Scalar(-8.50149846724410912031E-4), Scalar(4.22618223005546594270E-2),
        Scalar(-9.17480371773452345351E-2), Scalar(9.99999999999999994612E-1),
    };
    const Scalar AD[] = {
        Scalar(2.40372073066762605484E-11), Scalar(1.48864681368493396752E-9),
        Scalar(5.21265281010541664570E-8),  Scalar(1.27258478273186970203E-6),
        Scalar(2.32490249820789513991E-5),  Scalar(3.25524741826057911661E-4),
        Scalar(3.48805814657162590916E-3),  Scalar(2.79448531198828973716E-2),
        Scalar(1.58874241960120565368E-1),  Scalar(5.74918629489320327824E-1),
        Scalar(1.00000000000000000539E0),
    };
    const Scalar x2 = x * x;
    auto data_polevl_an =  _poevl<Scalar, 9>(x2, +9
    
    
]\\\\\\\AN);
auto data_polevl_ad = _poevl<Scalar, 10>(x2, AD);
    auto y = x * data_polevl_an;
    y = y / data_polevl_ad;
    return y;
  }

  template <typename Scalar>
  __aicore__ inline Scalar generic_dawsn_interval_2(const Scalar &x) {
    // Rational approximation on [3.25, 6.25)
    const Scalar BN[] = {
        Scalar(5.08955156417900903354E-1),  Scalar(-2.44754418142697847934E-1),
        Scalar(9.41512335303534411857E-2),  Scalar(-2.18711255142039025206E-2),
        Scalar(3.66207612329569181322E-3),  Scalar(-4.23209114460388756528E-4),
        Scalar(3.59641304793896631888E-5),  Scalar(-2.14640351719968974225E-6),
        Scalar(9.10010780076391431042E-8),  Scalar(-2.40274520828250956942E-9),
        Scalar(3.59233385440928410398E-11),
    };
    const Scalar BD[] = {
        Scalar(-6.31839869873368190192E-1),
        Scalar(2.36706788228248691528E-1),
        Scalar(-5.31806367003223277662E-2),
        Scalar(8.48041718586295374409E-3),
        Scalar(-9.47996768486665330168E-4),
        Scalar(7.81025592944552338085E-5),
        Scalar(-4.55875153252442634831E-6),
        Scalar(1.89100358111421846170E-7),
        Scalar(-4.91324691331920606875E-9),
        Scalar(7.18466403235734541950E-11),
    };
    const Scalar one = Scalar(1);
    const Scalar halfValue = Scalar(0.5);

    auto data_temp = x * x;
    data_temp = one / data_temp;
    auto data_rec = one / x;
    auto data_polevl_bn = _poevl<Scalar, 10>(data_temp, BN);
    data_polevl_bn = data_polevl_bn * data_temp;
    auto data_plevl_bd = _p1evl<Scalar, 10>(data_temp, BD);
    data_plevl_bd = data_plevl_bd * x;
    auto ret =  data_polevl_bn / data_plevl_bd;
    ret = data_rec + ret;
    return halfValue * ret;
  }

  template <typename Scalar>
  __aicore__ inline Scalar generic_dawsn_interval_3(const Scalar &x) {
    // Rational approximation on [6.25, 1.0e9)
    const Scalar CN[] = {
        Scalar(-5.90592860534773254987E-1), Scalar(6.29235242724368800674E-1),
        Scalar(-1.72858975380388136411E-1), Scalar(1.64837047825189632310E-2),
        Scalar(-4.86827613020462700845E-4),
    };
    const Scalar CD[] = {
        Scalar(-2.69820057197544900361E0),
        Scalar(1.73270799045947845857E0),
        Scalar(-3.93708582281939493482E-1),
        Scalar(3.44278924041233391079E-2),
        Scalar(-9.73655226040941223894E-4),
    };
    const Scalar one = Scalar(1);
    const Scalar halfValue = Scalar(0.5);

    auto data_temp = x * x;
    data_temp = 1 / data_temp;
    auto data_rec = 1 / x;
    auto data_polevl_cn = _poevl<Scalar, 4>(data_temp, CN);
    data_polevl_cn = data_polevl_cn * data_temp;
    auto data_plevl_cd = _p1evl<Scalar,5> (data_temp,CD);
    data_plevl_cd = data_plevl_cd * x;
    auto res = data_polevl_cn / data_plevl_cd;
    res = data_rec + res;
    res = res * halfValue;
    return res;
  }
  template <typename Scalar> __aicore__ inline Scalar dawsn_scalar(Scalar x) {
    const Scalar halfValue = Scalar(0.5);
    const Scalar a = Scalar(3.25);
    const Scalar b = Scalar(6.25);
    const Scalar c = Scalar(1.0e9);

    Scalar abs_x = x >= 0 ? x : -x;

    Scalar dawsn;
    if (abs_x < a) {
      dawsn = generic_dawsn_interval_1<Scalar>(abs_x);
    } else if (abs_x < b) {
      dawsn = generic_dawsn_interval_2<Scalar>(abs_x);
    } else if (abs_x < c) {
      dawsn = generic_dawsn_interval_3<Scalar>(abs_x);
    } else {
      dawsn = halfValue / x;
    }

    if (x < Scalar(0)) {
      dawsn = -dawsn;
    }
    return dawsn;
  }
  __aicore__ inline void Process2() {
    for (auto i = 0; i < size; ++i) {
      if constexpr (std::is_same_v<_DT_X, half>) {
        float v = GTensor(X).GetValue(i);
        v = dawsn_scalar(v);
        print("%f,", v);
        GTensor(Y).SetValue(i, half(v));
      } else {
        GTensor(Y).SetValue(i, dawsn_scalar(GTensor(X).GetValue(i)));
      }
    }
  }
};
} // namespace AscendC
extern "C" __global__ __aicore__ void dawsn(GM_ADDR x, GM_ADDR y,
                                            GM_ADDR workspace, GM_ADDR tiling) {
  GET_TILING_DATA(tiling_data, tiling);
  AscendC::Kernel<DTYPE_X, DTYPE_Y> op;
  op.Init(x, y, tiling_data);
  op.Process2();
}